package com.lancoo.ccas52.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lancoo.ccas52.entity.*;
import com.lancoo.ccas52.mapper.*;
import com.lancoo.ccas52.pojo.constant.GlobalConstant;
import com.lancoo.ccas52.pojo.response.basicPlatform.BaseGrade;
import com.lancoo.ccas52.pojo.vo.*;
import com.lancoo.ccas52.service.*;
import com.lancoo.ccas52.util.JdbcUtil;
import com.lancoo.ccas52.util.RedisUtil;
import com.lancoo.ccas52.util.TimeUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Future;
import java.util.stream.Collectors;

/**
 * @author quin.song
 * @since 2022-12-20
 */
@Service
@Slf4j
public class TaskInfoServiceImpl extends ServiceImpl<TaskInfoMapper, TaskInfo> implements TaskInfoService {

    @Resource
    private CollegeDealTimeMapper collegeDealTimeMapper;
    @Resource
    private CourseMapper courseMapper;
    @Resource
    private CourseApplyMapper courseApplyMapper;
    @Resource
    private CourseWeekMapper courseWeekMapper;
    @Resource
    private MajorMapper majorMapper;
    @Resource
    private PeriodSchemeMapper periodSchemeMapper;
    @Resource
    private PlanTeachingMapper planTeachingMapper;
    @Resource
    private PlanTeachingCourseSegmentMapper planTeachingCourseSegmentMapper;
    @Resource
    private PlanTeachingStudentMapper planTeachingStudentMapper;
    @Resource
    private RoomMapper roomMapper;
    @Resource
    private SegmentMapper segmentMapper;
    @Resource
    private SegmentApplyMapper segmentApplyMapper;
    @Resource
    private SegmentWeekMapper segmentWeekMapper;
    @Resource
    private SegmentSettingMapper segmentSettingMapper;
    @Resource
    private StudentMapper studentMapper;
    @Resource
    private TeacherMapper teacherMapper;
    @Resource
    private CourseSettingMapper courseSettingMapper;
    @Resource
    private RuleWeekDayMapper ruleWeekDayMapper;
    @Resource
    private RuleUsableMapper ruleUsableMapper;
    @Resource
    private SubClassMapper subClassMapper;
    @Resource
    private CollegeRoomMapper collegeRoomMapper;
    @Resource
    private CollegeStatusMapper collegeStatusMapper;

    @Resource
    private TeachingClassService teachingClassService;
    @Resource
    private BasicRuleService basicRuleService;

    @Resource
    private TaskProcessService taskProcessService;
    @Resource
    private CssTaskInfoService cssTaskInfoService;
    @Resource
    private JdbcUtil jdbcUtil;
    @Resource
    @Lazy
    private YunBasicService yunBasicService;
    @Resource
    private ScheduleService scheduleService;
    @Resource
    private ScheduleConflictInfoService scheduleConflictInfoService;
    @Resource
    private UserSettingService userSettingService;
    @Resource
    private TaskToGradeService taskToGradeService;

    @Resource
    private RedisUtil redisUtil;

    @Resource(name = "asyncExecutor")
    private ThreadPoolTaskExecutor executor;

    @Override
    public TaskInfo addTask(TaskInput taskInput) {
        TaskInfo taskInfo = new TaskInfo();
        BeanUtil.copyProperties(taskInput, taskInfo);
        taskInfo.setFlag(0);
        this.saveOrUpdate(taskInfo);
        Integer taskId = taskInfo.getTaskId();
        if (taskId > 0) {
            try {
                taskInput.getTaskToGrades().forEach(taskToGrade -> {
                    taskToGrade.setTaskId(taskId);
                });
                taskToGradeService.saveBatch(taskInput.getTaskToGrades());
                //init 插入流程数据
                taskProcessService.initProcess(taskInfo.getTaskId(), taskInfo.getMainFlow());
                //插入选课计划
                cssTaskInfoService.addCssTask(taskInfo);
                // 初始化基础规则
                basicRuleService.initRuleUsable(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
            } catch (Exception e) {
                //发生异常需要删除已同步的数据，因为多线程事务会失效。
                removeTaskByIds(Collections.singletonList(taskId));
                log.error("添加任务-{}-失败:{}", taskInfo, e.getMessage());
                throw new RuntimeException("添加任务失败:" + e.getMessage());
            }
        }
        return taskInfo;
    }

    public void testThread(TaskInfo taskInfo, List<TaskToGrade> taskToGrades) {
        List<String> gradeList = taskToGrades.stream().map(TaskToGrade::getGradeId).collect(Collectors.toList());
        yunBasicService.syncRoom(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
        yunBasicService.syncMajor(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm(), gradeList);
        yunBasicService.syncTeacher(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
        yunBasicService.syncStudent(taskInfo.getTaskId(), taskInfo.getSchoolId(), gradeList);
        yunBasicService.syncPlanTeaching(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm(), gradeList);
        yunBasicService.syncPeriodScheme(taskInfo.getTaskId(), taskInfo.getWeekSet(), taskInfo.getSchoolId(), taskInfo.getTerm());
        //插入选课计划
        cssTaskInfoService.addCssTask(taskInfo);
        //init 插入流程数据
        taskProcessService.initProcess(taskInfo.getTaskId(), taskInfo.getMainFlow());
        // 初始化基础规则
        basicRuleService.initRuleUsable(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
    }

    /**
     * https://blog.csdn.net/weixin_40918067/article/details/118979360
     * 多线程并发获取返回结果
     *
     * @throws Exception
     */
    public void syncThread(TaskInfo taskInfo, List<TaskToGrade> taskToGrades) throws Exception {
        /**
         * 同步基础平台数据分摊到各个模块
         * 设置各校区上课的学生 major room
         * 获取教学计划  plan_teaching plan_teaching_course_segment plan_teaching_student
         * 发布开课计划 syncTeacher syncStudent addCssTask(先放这里吧)
         * 课表编排进去时 syncPeriodScheme  initRuleUsable
         */
        List<String> gradeList = taskToGrades.stream().map(TaskToGrade::getGradeId).collect(Collectors.toList());
        List<Future> futures = new ArrayList<>();
        Future<?> future2 = executor.submit(() -> {
            yunBasicService.syncRoom(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
            yunBasicService.syncMajor(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm(), gradeList);
            yunBasicService.syncTeacher(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
        });
        Future<?> future3 = executor.submit(() -> {
            yunBasicService.syncStudent(taskInfo.getTaskId(), taskInfo.getSchoolId(), gradeList);
        });
        Future<?> future4 = executor.submit(() -> {
            yunBasicService.syncPlanTeaching(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm(), gradeList);
        });
        Future<?> future5 = executor.submit(() -> {
            yunBasicService.syncPeriodScheme(taskInfo.getTaskId(), taskInfo.getWeekSet(), taskInfo.getSchoolId(), taskInfo.getTerm());
        });
        Future<?> future6 = executor.submit(() -> {
            //插入选课计划
            cssTaskInfoService.addCssTask(taskInfo);
            //init 插入流程数据
            taskProcessService.initProcess(taskInfo.getTaskId(), taskInfo.getMainFlow());
            // 初始化基础规则
            basicRuleService.initRuleUsable(taskInfo.getTaskId(), taskInfo.getSchoolId(), taskInfo.getTerm());
        });

        futures.add(future2);
        futures.add(future3);
        futures.add(future4);
        futures.add(future5);
        futures.add(future6);
        for (Future future : futures) {
            while (true) {
                if (future.isDone() && !future.isCancelled()) {
                    future.get();
                    break;
                } else {
                    //每次轮询休息1毫秒（CPU纳秒级），避免CPU高速轮循耗空CPU---》新手别忘记这个
                    Thread.sleep(1);
                }
            }
        }
    }


    @Override
    public void removeTaskByIds(List<Integer> taskIds) {
        List<TaskInfo> taskInfos = this.lambdaQuery().in(TaskInfo::getTaskId, taskIds).list();
        this.removeByIds(taskIds);
        taskToGradeService.lambdaUpdate().in(TaskToGrade::getTaskId, taskIds).remove();
        //删除redis中的数据
        redisUtil.del(GlobalConstant.REDIS_ROUND_KEY);
        executor.submit(() -> {
            try {
                //延时删除。防止同步数据时未同步完成就执行该方法
                Thread.sleep(5000);
                taskProcessService.remove(new LambdaQueryWrapper<TaskProcess>().in(TaskProcess::getTaskId, taskIds));
                // 其他表数据
                collegeDealTimeMapper.delete(new LambdaQueryWrapper<CollegeDealTime>().in(CollegeDealTime::getTaskId, taskIds));
                courseMapper.delete(new LambdaQueryWrapper<Course>().in(Course::getTaskId, taskIds));
                courseApplyMapper.delete(new LambdaQueryWrapper<CourseApply>().in(CourseApply::getTaskId, taskIds));
                courseWeekMapper.delete(new LambdaQueryWrapper<CourseWeek>().in(CourseWeek::getTaskId, taskIds));
                courseSettingMapper.delete(new LambdaQueryWrapper<CourseSetting>().in(CourseSetting::getTaskId, taskIds));

                cssTaskInfoService.removeCssInfo(taskIds);

                majorMapper.delete(new LambdaQueryWrapper<Major>().in(Major::getTaskId, taskIds));
                collegeRoomMapper.delete(new LambdaQueryWrapper<CollegeRoom>().in(CollegeRoom::getTaskId, taskIds));
                planTeachingMapper.delete(new LambdaQueryWrapper<PlanTeaching>().in(PlanTeaching::getTaskId, taskIds));
                planTeachingCourseSegmentMapper.delete(new LambdaQueryWrapper<PlanTeachingCourseSegment>().in(PlanTeachingCourseSegment::getTaskId, taskIds));
                planTeachingStudentMapper.delete(new LambdaQueryWrapper<PlanTeachingStudent>().in(PlanTeachingStudent::getTaskId, taskIds));

                segmentMapper.delete(new LambdaQueryWrapper<Segment>().in(Segment::getTaskId, taskIds));
                segmentApplyMapper.delete(new LambdaQueryWrapper<SegmentApply>().in(SegmentApply::getTaskId, taskIds));
                segmentWeekMapper.delete(new LambdaQueryWrapper<SegmentWeek>().in(SegmentWeek::getTaskId, taskIds));
                segmentSettingMapper.delete(new LambdaQueryWrapper<SegmentSetting>().in(SegmentSetting::getTaskId, taskIds));

                studentMapper.delete(new LambdaQueryWrapper<Student>().in(Student::getTaskId, taskIds));

                taskInfos.forEach(taskInfo -> {
                    //删除学生选课数据
                    redisUtil.del(GlobalConstant.REDIS_PREFIX + taskInfo.getTaskId() + StrUtil.C_COLON + "TeachingClassStudent");
                    if (this.lambdaQuery().eq(TaskInfo::getSchoolId, taskInfo.getSchoolId()).eq(TaskInfo::getTerm, taskInfo.getTerm()).count() == 0) {
                        //多个任务共享资源 本学期没有任务时删除
                        teacherMapper.delete(new LambdaQueryWrapper<Teacher>().eq(Teacher::getSchoolId, taskInfo.getSchoolId()).eq(Teacher::getTerm, taskInfo.getTerm()));
                        roomMapper.delete(new LambdaQueryWrapper<Room>().eq(Room::getSchoolId, taskInfo.getSchoolId()).eq(Room::getTerm, taskInfo.getTerm()));
                        periodSchemeMapper.delete(new LambdaQueryWrapper<PeriodScheme>().eq(PeriodScheme::getSchoolId, taskInfo.getSchoolId()).eq(PeriodScheme::getTerm, taskInfo.getTerm()));
                        ruleWeekDayMapper.delete(new LambdaQueryWrapper<RuleWeekDay>().eq(RuleWeekDay::getSchoolId, taskInfo.getSchoolId()).eq(RuleWeekDay::getTerm, taskInfo.getTerm()));
                        ruleUsableMapper.delete(new LambdaQueryWrapper<RuleUsable>().eq(RuleUsable::getSchoolId, taskInfo.getSchoolId()).eq(RuleUsable::getTerm, taskInfo.getTerm()));
                    }
                });

                teachingClassService.deleteClassRelated(taskIds);
                subClassMapper.delete(new LambdaQueryWrapper<SubClass>().in(SubClass::getTaskId, taskIds));
                basicRuleService.removeAllRule(taskIds);
                collegeStatusMapper.delete(new LambdaQueryWrapper<CollegeStatus>().in(CollegeStatus::getTaskId, taskIds));
                scheduleService.lambdaUpdate().in(Schedule::getTaskId, taskIds).remove();
                scheduleConflictInfoService.lambdaUpdate().in(ScheduleConflictInfo::getTaskId, taskIds).remove();

                userSettingService.lambdaUpdate().in(UserSetting::getTaskId, taskIds).remove();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }

    @Override
    public List<GradeDto> getGrade(String schoolId, String term) {
        List<GradeDto> gradeList = new ArrayList<>();
        //基础平台获取年级信息 开启计划只有本学期，下学期.暂时不考虑同步年级信息
        List<BaseGrade> baseGrades = yunBasicService.getGrade(schoolId);
        baseGrades.sort(Comparator.comparing(BaseGrade::getOrderNo));
        baseGrades.forEach(baseGrade -> {
            GradeDto gradeDto = new GradeDto();
            gradeDto.setGradeId(baseGrade.getGradeID());
            gradeDto.setGradeName(baseGrade.getGradeName());
            gradeList.add(gradeDto);
        });

        //将已选的年级标记一下
        List<TaskInfo> taskInfos = this.lambdaQuery().eq(TaskInfo::getSchoolId, schoolId).eq(TaskInfo::getTerm, term).list();
        if (CollUtil.isEmpty(taskInfos)) {
            return gradeList;
        }
        taskToGradeService.lambdaQuery().in(TaskToGrade::getTaskId, taskInfos.stream().map(TaskInfo::getTaskId).collect(Collectors.toList())).list().forEach(taskToGrade -> {
            gradeList.forEach(baseGrade -> {
                if (baseGrade.getGradeId().equals(taskToGrade.getGradeId())) {
                    baseGrade.setSelected(true);
                }
            });
        });
        return gradeList;
    }

    @Override
    @Cacheable(cacheNames = "countWeek", key = "#taskId")
    public WeekDateVo countWeek(Integer taskId) {
        WeekDateVo weekDateVo = new WeekDateVo();
        List<WeekDateVo.WeekDate> weekDates = new ArrayList<>();

        TaskInfo taskInfo = this.getById(taskId);
        LocalDateTime termBeginTime = taskInfo.getTermBeginTime();
        LocalDateTime termEndTime = taskInfo.getTermEndTime();
        long totalWeek = TimeUtils.getTotalWeek(termBeginTime, termEndTime);
        weekDateVo.setWeekCount(totalWeek);
        for (int i = 1; i <= totalWeek; i++) {
            LocalDateTime startWeek;
            LocalDateTime endWeek;
            if (i == 1) {
                //第一周
                startWeek = termBeginTime;
                endWeek = startWeek.with(DayOfWeek.SUNDAY);
            } else if (i == totalWeek) {
                //第二周
                endWeek = termEndTime;
                startWeek = endWeek.with(DayOfWeek.MONDAY);
            } else {
                //中间周
                startWeek = termBeginTime.with(DayOfWeek.MONDAY).plusWeeks(i - 1);
                endWeek = startWeek.with(DayOfWeek.SUNDAY);
            }
            weekDates.add(new WeekDateVo.WeekDate().setWeek(i).setStart(startWeek).setEnd(endWeek));
        }
        weekDateVo.setWeekDates(weekDates);
        return weekDateVo;
    }

    @Override
    public List<CollegeDto> getCollegeByTaskId(Integer taskId, Integer module) {
        List<CollegeDto> collegeDtoList = new ArrayList<>();
        switch (module) {
            case 1:
                collegeDtoList = majorMapper.getCollegeByTaskId(taskId);
                break;
            case 2:
                collegeDtoList = courseMapper.getCollegeByTaskId(taskId);
                break;
            case 3:
                collegeDtoList = segmentMapper.getCollegeByTaskId(taskId);
                break;
            case 4:
                collegeDtoList = studentMapper.getCollegeByTaskId(taskId);
                break;
            default:
                //直接从基础平台获取
                return collegeDtoList;
        }
        return collegeDtoList;
    }

    @Override
    public List<GradeDto> getGradeByTaskId(Integer taskId, Integer module) {
        List<GradeDto> gradeDtoList = new ArrayList<>();
        switch (module) {
            case 1:
                gradeDtoList = majorMapper.getGrade(taskId, null, null);
                break;
            case 2:
                //no grade
                gradeDtoList = jdbcUtil.queryGrade("course", taskId);
                break;
            case 3:
                //no grade
                gradeDtoList = jdbcUtil.queryGrade("segment", taskId);
                break;
            case 4:
                gradeDtoList = studentMapper.getGrade(taskId, null, null);
                break;
            default:
                //直接从基础平台获取
                return gradeDtoList;
        }
        return gradeDtoList;
    }

    @Override
    public List<MajorDto> getMajorByTaskId(Integer taskId, Integer module, String collegeId) {
        List<MajorDto> majorDtoList = new ArrayList<>();
        switch (module) {
            case 1:
                majorDtoList = majorMapper.getMajor(taskId, collegeId);
                break;
            case 2:
                //课程没有专业
                majorDtoList = jdbcUtil.queryMajor("course", taskId);
                break;
            case 3:
                //no major
                majorDtoList = jdbcUtil.queryMajor("segment", taskId);
                break;
            case 4:
                majorDtoList = studentMapper.getMajor(taskId, collegeId);
                break;
            default:
                //直接从基础平台获取
                return majorDtoList;
        }
        return majorDtoList;
    }

    @Override
    public List<ClassDto> getClassByTaskId(Integer taskId, Integer module, String collegeId, String majorId, String gradeId) {
        List<ClassDto> classDtoList = new ArrayList<>();
        switch (module) {
            case 1:
                classDtoList = jdbcUtil.queryClass("major", taskId);
                break;
            case 2:
                // no class
                classDtoList = jdbcUtil.queryClass("course", taskId);
                break;
            case 3:
                //no class
                classDtoList = jdbcUtil.queryClass("segment", taskId);
                break;
            case 4:
                classDtoList = studentMapper.getClassDto(taskId, collegeId, majorId, gradeId);
                break;
            default:
                //直接从基础平台获取
                return classDtoList;
        }
        return classDtoList;
    }
}
